Skip to content

feat: add parallel chunk processing for large documents in transformations - #529

Open
kevincolten wants to merge 14 commits into
lfnovo:mainfrom
Notebooker-ai:feat/small-context-chunking
Open

feat: add parallel chunk processing for large documents in transformations#529
kevincolten wants to merge 14 commits into
lfnovo:mainfrom
Notebooker-ai:feat/small-context-chunking

Conversation

@kevincolten

@kevincolten kevincolten commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Description

Enable smaller context models to process large documents by automatically chunking content that exceeds context limits and processing chunks in parallel using LangGraph's Send API.

How it works:

  1. try_full_content - Attempts to process entire document optimistically
  2. On context limit error, parses the error to calculate optimal chunk size
  3. fan_out_chunks - Creates parallel Send() calls for each chunk
  4. process_chunk - Processes chunks concurrently via LangGraph Send API
  5. synthesize_results - Merges chunk results into unified output

Related Issue

Fixes #990

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Performance improvement

How Has This Been Tested?

  • Tested locally with development setup
  • Added new unit tests
  • Existing tests pass (uv run pytest)
  • Updated existing tests for new imports
  • Manual testing performed (describe below)

Test Details:

  • Added 23 new unit tests for chunking functionality (31 total graph tests)
  • All tests pass
  • Real-world test with 145K token document:
    • Successfully split into 2 chunks
    • Parallel processing completed in ~5.5 minutes
    • Synthesis completed in ~26 seconds
    • Total: ~6 minutes vs ~11+ minutes sequential

Design Alignment

Which design principles does this PR support? (See DESIGN_PRINCIPLES.md)

  • Simplicity Over Features
  • Multi-Provider Flexibility
  • Async-First for Performance

Explanation:

  • Simplicity: Automatic fallback with no configuration needed - just works
  • Multi-Provider Flexibility: Enables smaller/cheaper models to handle large documents that previously required expensive large-context models
  • Async-First: Parallel chunk processing via LangGraph Send API maximizes performance

Checklist

Code Quality

  • My code follows PEP 8 style guidelines (Python)
  • I have added type hints to my code (Python)
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I ran linting: make ruff or ruff check . --fix

Documentation

  • I have added/updated docstrings for new/modified functions
  • I have added comments to complex logic

Screenshots (if applicable)

N/A - Backend changes only

Additional Context

Files Modified:

  • open_notebook/graphs/transformation.py - Full restructure to use Send API
  • tests/test_graphs.py - Updated imports for new function names

New Components:

  • ChunkResult / ChunkState - TypedDicts for parallel processing
  • try_full_content() - Optimistic processing with error-based fallback
  • fan_out_chunks() - Conditional edge creating Send objects
  • process_chunk() - Individual chunk processor
  • synthesize_results() - Result aggregator using Annotated[list, operator.add]

Pre-Submission Verification

Before submitting, please verify:

  • I have read CONTRIBUTING.md
  • I have read DESIGN_PRINCIPLES.md
  • I have not included unrelated changes in this PR
  • My PR title follows conventional commits format (e.g., "feat: add user authentication")

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 6 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="open_notebook/utils/token_utils.py">

<violation number="1" location="open_notebook/utils/token_utils.py:153">
P2: `is_context_limit_error` matches generic substrings like "limit" and "exceeded", so common rate-limit errors (e.g., "Rate limit exceeded") will be treated as context-length errors. In transformation, that triggers parallel chunk retries, likely worsening rate limiting and causing retry spikes.</violation>

<violation number="2" location="open_notebook/utils/token_utils.py:268">
P2: Sentence-level splitting can still create chunks that exceed max_tokens when a single sentence is longer than the limit, violating the function’s contract and potentially re-triggering context-limit errors.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread open_notebook/utils/token_utils.py Outdated
Comment thread open_notebook/utils/token_utils.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="open_notebook/utils/token_utils.py">

<violation number="1" location="open_notebook/utils/token_utils.py:310">
P2: `current_chunk` is set to a list of words, but remaining chunks are joined with `"\n\n"`, so an oversized sentence fragment at the end will be emitted with double newlines between every word instead of spaces.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread open_notebook/utils/token_utils.py Outdated
… synthesis

When a transformation's full content exceeds the model's context window, split
it into token-sized chunks, process them in parallel, and synthesize the partial
results back into one output. The synthesis is reduced hierarchically — chunk
results are batched to fit a token budget and combined in rounds — so merging
many (or large) results never overflows the context window either.

- token_utils: add context-limit error parsing (OpenAI/Anthropic/Google),
  is_context_limit_error, token-aware text chunking, and output-buffer helpers.
- transformation: try_full_content -> fan_out_chunks -> process_chunk ->
  synthesize_results (with budgeted reduce). Preserves the single-call fast path
  and upstream's classify_error behavior.
- tests: chunking helpers, fan-out routing, and a regression test asserting
  synthesis batches instead of overflowing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kevincolten
kevincolten force-pushed the feat/small-context-chunking branch from 3c76e16 to 19448a1 Compare June 14, 2026 04:35

@lfnovo lfnovo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kevincolten — this is a genuinely nice piece of work: the optimistic-full → chunk-on-context-error → parallel Send → hierarchical synthesis flow is well structured, it keeps Prompter intact, doesn't disturb the token_count fix, and it's well tested (the 145K-token real run is a great touch). Two blocking items before it can merge, plus a few smaller notes.

Blocking

1. Output cap silently drops from 8192 → 4096 on the normal (non-chunked) path.
try_full_content sets output_buffer = DEFAULT_OUTPUT_TOKENS (4096) and calls provision_langchain_model(..., max_tokens=output_buffer). main currently uses max_tokens=8192. Since try_full runs for every transformation (not just large docs), any transformation whose output exceeds 4096 tokens would now be truncated — a silent regression on the common path. Please preserve the current 8192 default for the full-content attempt (or make the output budget configurable and default it to 8192).

2. Module-level asyncio.Semaphore bound at import.

_chunk_semaphore = asyncio.Semaphore(_CHUNK_CONCURRENCY_LIMIT)  # module scope

There are no other module-level asyncio primitives in the codebase, and this one is a footgun: an asyncio.Semaphore binds to the first event loop it's awaited from. This graph is imported by the worker (run_transformation_command) and can also be exercised from the API's loop; awaiting the same module-level semaphore from two loops raises RuntimeError: bound to a different event loop. Please create it inside the function (or lazily per-invocation) rather than at import.

Non-blocking (worth addressing)

  1. Context-limit detection parses provider error strings (is_context_limit_error / get_context_limit_from_error). That's inherently provider-format-dependent and will silently fall back to DEFAULT_CONTEXT_LIMIT (8192) when a wording doesn't match, which can mis-size chunks. A short comment on the supported formats + how the fallback behaves would help future maintenance.

  2. Chunk meta-prefix leaks into content. Each chunk is sent as "[Processing section X of N from a larger document]\n\n{chunk}". For summarization that's fine, but for extraction-style transformations that instruction text can bleed into or skew the output. Consider putting the "section X of N" hint in the system prompt instead of the user content.

  3. _CHUNK_CONCURRENCY_LIMIT = 3 is a second concurrency layer on top of the worker's own limit (OPEN_NOTEBOOK_WORKER_MAX_TASKS, see #893). Worth a comment noting the interaction, or deriving it from the same config.

Process note

There's no linked issue — features go through an approved issue first (CONTRIBUTING). The feature itself is well-aligned, so this is easy to formalize; I'll get an issue opened to track it. Once (1) and (2) are addressed I'm happy to re-review. (Heads up: maintainerCanModify is off on this PR, so these need to come from your side.)

@lfnovo

lfnovo commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Opened #990 to track this feature (with the review notes captured as design/acceptance criteria) — this PR is the implementation for it. Once the two blocking items above are addressed, happy to re-review. 🙏

- Restore the 8192 output cap on the full-content path: bump
  DEFAULT_OUTPUT_TOKENS from 4096 to 8192 so the optimistic attempt
  matches the pre-chunking max_tokens and never truncates outputs on
  the common path.
- Create chunk semaphores lazily per event loop instead of at module
  import: an asyncio.Semaphore binds to the loop it is first awaited
  from, and the graph runs from both the worker's and the API's loops.
- Move the "section X of N" hint from the user content into the system
  prompt so it can't bleed into extraction-style outputs.
- Document the supported provider error formats and the
  DEFAULT_CONTEXT_LIMIT fallback behavior in token_utils.
- Note the interaction between _CHUNK_CONCURRENCY_LIMIT and the
  worker's task limit (OPEN_NOTEBOOK_WORKER_MAX_TASKS).
- Add tests for the 8192 cap, verbatim chunk content, and per-loop
  semaphores.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread open_notebook/graphs/transformation.py
@kevincolten

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @lfnovo! All items addressed in 69e5156:

Blocking:

  1. 8192 output cap restoredDEFAULT_OUTPUT_TOKENS bumped to 8192, so the full-content attempt provisions with the same max_tokens=8192 as before this PR. The chunked path is unaffected (it computes its own budget via calculate_output_buffer). Added a test asserting the 8192 cap on the full-content path.
  2. Module-level semaphore removed — chunk semaphores are now created lazily per event loop (WeakKeyDictionary keyed by the running loop), so the worker's and API's loops each get their own, while the parallel Send nodes of a single invocation still share one bound. Added a test asserting distinct semaphores across loops.

Non-blocking:

  1. Documented the supported provider error formats (OpenAI/Anthropic/Google + generic patterns) and the conservative DEFAULT_CONTEXT_LIMIT fallback behavior in token_utils.py.
  2. Moved the "section X of N" hint from the user content into the system prompt — chunks are now sent verbatim, with a test pinning that.
  3. Added a comment on _CHUNK_CONCURRENCY_LIMIT noting it multiplies with the worker's task limit (OPEN_NOTEBOOK_WORKER_MAX_TASKS, Fix: Sequential processing mode for single-GPU setups (prevents LLM rate limits) #893).

Also linked the PR to #990. All 204 tests pass, ruff clean. Ready for re-review 🙏

@kevincolten
kevincolten requested a review from lfnovo July 3, 2026 15:15
…chunking

# Conflicts:
#	open_notebook/graphs/transformation.py
The chunking work split run_transformation into try_full_content ->
process_chunk -> synthesize_results. try_full_content owns the
non-chunking path and its add_insight() call, so it is where upstream's
propagation contract now lives.
- parse_context_limit_error: widen to Optional[Tuple[Optional[int], int]].
  The docstring already documented that tokens_sent may be None when only
  the limit is parseable, and its sole caller (get_context_limit_from_error)
  already returns that wider type.
- test_graphs: narrow await_args before attribute access, and type the
  process_chunk state as ChunkState.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread tests/test_add_insight_failure_propagation.py

@lfnovo lfnovo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for keeping this alive and for addressing both blockers from the last round (8192 default restored, per-event-loop semaphore). Issue #990 is ready and this is the implementation, so let's get it over the line. Two things remain, both small, and one is newly important:

  1. Wrap the chunk-path LLM calls in classify_error(). process_chunk and _synthesize_once let raw provider exceptions escape. #1275 (merged this week) added ContextLengthExceededError to the retry blocklists in commands/source_commands.py, so an unclassified exception from a chunk node will now be retried by surreal-commands with the full exponential budget, which is exactly the behavior #1275 removed. Route them through the same classify_errorraise error_class(...) from e pattern the single-shot path uses in open_notebook/graphs/transformation.py.
  2. Reuse open_notebook/utils/error_classifier.py instead of a second keyword list. token_utils.is_context_limit_error duplicates the context-limit detection main already owns; the two will drift. Build one on the other.

Two non-blocking notes for your judgment: calculate_output_buffer(8192) yields ~819 output tokens per chunk when the error wording doesn't parse, which seems likely to truncate chunk results; and the synthesis prompt ("merge, remove redundancy") is right for summaries but lossy for extraction-style transformations, silently. A sentence in the docs about that trade-off would be enough.

Please rebase on main and add a CHANGELOG line under Unreleased → Added.

kevincolten and others added 3 commits September 5, 2026 09:18
…detection on error_classifier

Addresses the 2026-09-05 review on lfnovo#529 (issue lfnovo#990).

- process_chunk and _synthesize_once now route provider exceptions through
  classify_error() via a shared _invoke_llm helper, so a context-length
  rejection from a chunk surfaces as ContextLengthExceededError and the
  worker's stop_on list (lfnovo#1275) stops retrying it; other failures get the
  same sanitized, typed errors as the single-shot path.
- token_utils.is_context_limit_error is a thin wrapper over classify_error;
  the duplicate keyword lists are gone. error_classifier's context-length
  rule gains the Anthropic/Google/"context window" wordings that only lived
  in token_utils. HTTP 413 / "request too large" deliberately no longer
  triggers chunking (payload limit, not a token window).
- classify_error matches numeric status codes ("401", "429", "500", ...) as
  standalone numbers. Substring matching read "429" out of token counts
  like "142900 tokens > 200000 maximum", turning a context-length error
  into a RateLimitError that was retried instead of chunked (same class of
  bug as lfnovo#1303).
- Chunk output budget floors at 2048 tokens (capped at a quarter of the
  window) instead of 10% of an unparsed 8192 default (819 tokens).
- Google's current "exceeds the maximum number of tokens allowed (N)"
  wording is parsed for the limit.
- Docs: Large Documents section in the transformations guide covering the
  synthesis trade-off for extraction-style prompts. CHANGELOG entry.
- Tests: classification/parsing tables, chunk and synthesis error paths,
  try_full_content chunk fallback; renamed the node-level propagation test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011V8PghiyLvfhK5meqkU4QQ
@kevincolten

kevincolten commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in 8b0084a. The branch is current with main via merge (it already carries merge commits, so no force-push).

1. Chunk-path errors are classified

process_chunk and _synthesize_once now share an _invoke_llm helper that wraps provision + ainvoke in the same classify_error()raise error_class(...) from e pattern as the single-shot path. A context-length rejection from a chunk surfaces as ContextLengthExceededError and hits the #1275 stop_on lists instead of the retry budget.

2. One keyword list

  • token_utils.is_context_limit_error is a thin wrapper over classify_error; its own keyword lists are gone.
  • try_full_content calls classify_error once and branches on the class.
  • The context-length rule in error_classifier.py gained the wordings only token_utils knew (prompt is too long, input token count, context window, too many tokens, input too long).

Two side effects to flag:

  • HTTP 413 / "request too large" no longer triggers chunking. It is a payload limit, not a token window, so it stays ExternalServiceError. Easy to add back if you'd rather chunk on it.
  • classify_error now matches status codes (401, 429, 500, …) as standalone numbers. With substring matching, "prompt is too long: 142900 tokens > 200000 maximum" became a RateLimitError because of the 429 inside the token count, so it never chunked and the worker retried it. Same bug class as fix: stop Anthropic retired Haiku ids from looking like HTTP 403 #1303; tests cover both directions.

Non-blocking notes

  • calculate_output_buffer floors at 2048 tokens (capped at a quarter of the window). An unparsed 8192 default now gives 2048 output tokens per chunk instead of 819.
  • The synthesis trade-off is documented in a new "Large Documents" section of the transformations guide.
  • Gemini's current wording (exceeds the maximum number of tokens allowed (N)) now yields the limit instead of the default.

CHANGELOG line added under Unreleased → Added. New tests/test_token_utils.py, a classification table in test_context_length_no_retry.py, chunk/synthesis error paths in test_graphs.py, and the node-level propagation test renamed. Full suite, ruff and mypy pass.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 9 files (changes from recent commits).

Confidence score: 4/5

  • open_notebook/utils/token_utils.py may stop recognizing some provider context-limit errors when the message says only “exceeds the maximum,” causing the expected context-length handling or fallback to be skipped—restore the previously supported wording in is_context_limit_error().
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="open_notebook/utils/token_utils.py">

<violation number="1" location="open_notebook/utils/token_utils.py:186">
P2: When a provider reports a context rejection as `exceeds the maximum` without `context` or `input token count`, `is_context_limit_error()` now returns false. Add the previously supported context wording to the shared classifier rule so these errors still trigger chunking.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread open_notebook/utils/error_classifier.py
generic external error and return False here, so callers treat them as a
regular failure rather than chunking on them."""
error_class, _ = classify_error(error)
return issubclass(error_class, ContextLengthExceededError)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a provider reports a context rejection as exceeds the maximum without context or input token count, is_context_limit_error() now returns false. Add the previously supported context wording to the shared classifier rule so these errors still trigger chunking.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At open_notebook/utils/token_utils.py, line 186:

<comment>When a provider reports a context rejection as `exceeds the maximum` without `context` or `input token count`, `is_context_limit_error()` now returns false. Add the previously supported context wording to the shared classifier rule so these errors still trigger chunking.</comment>

<file context>
@@ -160,58 +172,18 @@ def parse_context_limit_error(error: Exception) -> Optional[Tuple[Optional[int],
+    generic external error and return False here, so callers treat them as a
+    regular failure rather than chunking on them."""
+    error_class, _ = classify_error(error)
+    return issubclass(error_class, ContextLengthExceededError)
 
 
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partly valid. The bare phrase was dropped on purpose: "exceeds the maximum" also describes upload sizes and request counts, and the old list only avoided that by checking a separate non-context blocklist first. 178b33c re-adds it as a token-qualified pattern ("token(s)" within 40 chars of "exceeds the maximum", either order), so "9000 tokens exceeds the maximum of 8192" chunks while "file exceeds the maximum upload size" does not. Tests cover both. Also added Bedrock's "Input is too long for requested model".

…ength rule, re-add token-qualified "exceeds the maximum"

Follow-up to cubic's review of 8b0084a:

- "too many tokens per minute" style throttles matched the new "too many
  tokens" context keyword. The rate-limit rule (which runs first) now also
  matches "tokens per minute", "tokens per min" and "(tpm)", so token-rate
  limits stay retryable and never trigger chunking.
- The bare "exceeds the maximum" wording from the old token_utils list is
  back as a compiled pattern that requires "token(s)" within 40 chars, so
  "9000 tokens exceeds the maximum of 8192" chunks while "file exceeds the
  maximum upload size" does not. Rules may now hold regex patterns next to
  substrings; _keyword_matches searches them.
- Bedrock's "Input is too long for requested model" is recognised.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011V8PghiyLvfhK5meqkU4QQ

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Confidence score: 5/5

  • In tests/test_context_length_no_retry.py, test_status_codes_still_match_as_standalone_numbers now also contains token-rate throttle cases, which mixes unrelated concerns and could make failures harder to diagnose; move those cases into a dedicated test.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/test_context_length_no_retry.py">

<violation number="1" location="tests/test_context_length_no_retry.py:113">
P3: The two new token-rate throttle cases were appended to `test_status_codes_still_match_as_standalone_numbers`, a test whose stated concern (and name) is that status codes like 429 match only as standalone numbers. These cases contain no status code; they pin token-throttle wording to RateLimitError instead. They test a different concern and read better as their own parametrized test (or a rename), so a classification regression in either path isn't attributed to the right guard.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

# Token-rate throttles mention tokens but are transient, not a
# context window: they must stay retryable and must not chunk.
("Too many tokens per minute for this model, slow down.", RateLimitError),
("Request too large for model on tokens per min (TPM): Limit 6000", RateLimitError),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The two new token-rate throttle cases were appended to test_status_codes_still_match_as_standalone_numbers, a test whose stated concern (and name) is that status codes like 429 match only as standalone numbers. These cases contain no status code; they pin token-throttle wording to RateLimitError instead. They test a different concern and read better as their own parametrized test (or a rename), so a classification regression in either path isn't attributed to the right guard.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_context_length_no_retry.py, line 113:

<comment>The two new token-rate throttle cases were appended to `test_status_codes_still_match_as_standalone_numbers`, a test whose stated concern (and name) is that status codes like 429 match only as standalone numbers. These cases contain no status code; they pin token-throttle wording to RateLimitError instead. They test a different concern and read better as their own parametrized test (or a rename), so a classification regression in either path isn't attributed to the right guard.</comment>

<file context>
@@ -102,6 +107,10 @@ def test_provider_wordings_are_context_length(self, message):
+            # Token-rate throttles mention tokens but are transient, not a
+            # context window: they must stay retryable and must not chunk.
+            ("Too many tokens per minute for this model, slow down.", RateLimitError),
+            ("Request too large for model on tokens per min (TPM): Limit 6000", RateLimitError),
             ("Error code: 401 - invalid api key", AuthenticationError),
             ("Error code: 503 - service unavailable", ExternalServiceError),
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: parallel chunk processing for large-document transformations

2 participants